/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 
package com.puny.mvc.views
{
	import com.puny.mvc.BaseObject;
	import com.puny.mvc.MvcEvent;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.media.Sound;
	import flash.net.URLRequest;
	
	
	/**
	 * $Revision: 798 $ <br/>
	 * This defines abstract class of views for this MVC archtecture.
	 * 
	 * This is a chart that shows you the method order to be invoked.
	 * Each method can be overrode when you need.
	 * <img src="../../../../images/lifecycle.png"/>
	 * 
	 * @example Here is a sample code for a view class which uses an external swf.
	 * <listing version="3.0">
	 * public calss SampleView extends View {
	 *		public function SampleView() {
	 * 			super("assets/my_assets.swf");
	 *		}
	 * }
	 * </listing>
	 * @example Here is another sample code for a view class which uses an embedded swf.
	 * <listing version="3.0">
	 * public class EmbededView extends CommonView {
	 *		[Embed(source="assets/my_assets.swf")]
	 *		private var swf:Class;
	 *
	 *		public function EmbededView() {
	 *			super(new swf());
	 *		}
	 * }
	 * </listing>
	 * <listing version="3.0">
	 * public class EmbededView extends CommonView {
	 *		[Embed(source="assets/my_assets.swf")]
	 *		private var swf:Class;
	 *
	 *		public function EmbededView() {
	 *			super(swf);
	 *		}
	 * }
	 * </listing>
	 * 
	 */
	public class View extends BaseObject
	{
		private var _loader:Loader
		private var _bytesLoaded:uint;
		private var _bytesTotal:uint;
		private static var _loaders:Object;
		
		public function View(src:Object=null)
		{
			if (_loaders == null) {
				_loaders = new Object();
			}
			super(src);
//			boot(src);
		}
		
		/**
		 * To initialize an embeded swf.
		 * When this view is added on the stage and the given swf is loaded successfully,
		 * a READY event will be fired.
		 * 
 		 * @param swf A MovieClipAsset instance which is embeded  
		 */
		private function embedded(swf:MovieClip):void {
			_loader = Loader(swf.getChildAt(0));
			_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded, false, 0, true);
		}
		
		/**
		 * To load external a swf file. completeHander, progressHandler, loadChildHandler or errorHandler
		 * will be invoked after this method was called. After a swf was loaded succesfully, loadChildren
		 * will be called.
		 *
		 * When this view is added on the stage and the given swf is loaded successfully,
		 * a READY event will be fired.
		 * 
		 * @param name a swf file name, a MovieClipAsset instance, or a Class name
		 * @see com.puny.mvc.views.View#completeHandler
		 * @see com.puny.mvc.views.View#progressHandler
		 * @see com.puny.mvc.views.View#errorHandler
		 * @see com.puny.mvc.views.View#loadChildren
		 */
		final override protected function boot(filename:Object=null):void {
			var created:Boolean = false;
			if (filename==null) {
				loaded();
				return;
			} else if (filename is Class) {
				if (_loaders[filename] == null) {
					embedded(new filename());
					_loaders[filename] = _loader;
				} else {
					_loader = _loaders[filename];
					if(_loader.content == null) {
						_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded, false, 0, true);
					} else {
						loaded();
					}
				}
			} else if (filename is MovieClip) {
				embedded(filename as MovieClip);
			} else if (filename is String || filename is XMLList) {
				_loader = _loaders[filename];
				if (_loader != null&&_loader.content != null) {
					_bytesLoaded = 1;
					_bytesTotal = 1;
					loaded();
				} else {
					if (_loader == null) {
						_loader = new Loader();
						_loaders[filename] = _loader;
						created = true;
					}
					_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler, false, 0, true);
					_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded, false, 0, true);
					_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, errorHandler, false, 0, true);
					if (created) {
						_loader.load(new URLRequest(filename is String ? filename as String : filename as XMLList));
					}
				}
			} else {
				super.boot(filename);
			}
		}

		final override protected function loaded(event:Event=null):void {
			if (event != null) {
				_loader.contentLoaderInfo.removeEventListener(Event.INIT, loaded);
			}
			super.loaded(event);
		}
		/**
		 * Empty method when IOErrorEvent.IOERROR is dispatched
		 */
		protected function errorHandler(event:IOErrorEvent):void {
			trace(event);
		}
		
		/**
		 * Empty method when ProgressEvent.PROGRESS is dispatched
		 */
		private function progressHandler(event:ProgressEvent):void {
			_bytesLoaded = event.bytesLoaded;
			_bytesTotal = event.bytesTotal;
			dispatchEvent(new MvcEvent(MvcEvent.LOADING));
			trace(this,"---",_bytesLoaded,_bytesTotal);
		}
		
		/**
		 * Returns bytes that is loaded.
		 */
		override public function get bytesLoaded():uint {
			return _bytesLoaded;
		}
		
		/**
		 * Returns bytes that is total of the swf.
		 */
		override public function get bytesTotal():uint {
			return _bytesTotal;
		}
		
		/**
		 * Return percentage that is loaded.
		 */
		public function get percentageofLoaded():uint {
			return uint(_bytesLoaded * 100 / _bytesTotal);	
		}	
					
		/**
		 * Load an exported asset form the loaded swf file as a MovieClip
		 * 
		 * @param name Will be an exported name as a class in the loaded swf file.
		 * @param add Will be added in this view class immediately if it's true.
		 * @param cache <code>cacheAsBitmap</code> of this MovieClip is true if it's true.
		 * @return Instance of a MovieClip
		 */
		protected function getMovieClip(name:String,add:Boolean=true,cache:Boolean=true):MovieClip {
			var mcClass:Class = getClass(_loader,name);
			var mc:MovieClip = MovieClip(new mcClass());
			mc.cacheAsBitmap = cache;
			if (add) {
				addChild(mc);
			}
			mc.gotoAndStop(1);
			return mc;
		}
		
		/**
		 * Load an exported asset form the loaded swf file as a Sprite
		 * 
		 * @param name should be an exported name as a class in the loaded swf file.
		 * @param add should be added in this view class immediately if it's true.
		 * @param cache <code>cacheAsBitmap</code> of this Sprite is true if it's true.
		 * @return Instance of a Sprite
		 */
		protected function getSprite(name:String,add:Boolean=true,cache:Boolean=true):Sprite {
			var spClass:Class = getClass(_loader,name);
			var sp:Sprite = Sprite(new spClass());
			sp.cacheAsBitmap = cache;
			if (add) {
				addChild(sp);
			}
			return sp;
		}
		
		/**
		 * 
		 * @param name should be an exported name as a class in the loaded swf file.
		 * @return an instance of Sound class.
		 * 
		 */
		protected function getSound(name:String):Sound {
			var spClass:Class = getClass(_loader,name);
			var sp:Sound = Sound(new spClass());
			return sp;
		}
		
		/**
		 * 
		 * @param name should be an exported name as a class in the loaded swf file.
		 * @return an instance of BitmapData
		 * 
		 */
		protected function getBitmapData(name:String):BitmapData {
			var spClass:Class = getClass(_loader,name);
			//arguments, (1,1) for spClass are dummy
			var bd:BitmapData = BitmapData(new spClass(1,1));
			return bd;
		}

		/**
		 * 
		 * @return a main timeline asset in the loaded swf file.
		 * 
		 */
		protected function content():DisplayObject {
			if(_loader.content is Bitmap) {
				var bmp:Bitmap = _loader.content as Bitmap;
				if (!(bmp.parent is Loader)) {
					return new Bitmap(bmp.bitmapData);
				}
			}
			return _loader.content;
		}
		
		/**
		 * 
		 * @param name exported name
		 * @return Class
		 * 
		 */
		protected function getExportedClass(name:String):Class {
			return getClass(_loader,name);
		}
		
		private function getClass(target:Object,name:String):Class {
//			trace(target.content.loaderInfo.sameDomain);
			return target.content.loaderInfo.applicationDomain.getDefinition(name);
		}

		protected function getFrameEnds(mc:MovieClip):Object {
			var object:Object = new Object();
			for(var i:int = 1;i < mc.totalFrames+1;i++) {
				mc.gotoAndStop(i);
				object[mc.currentLabel] = mc.currentFrame;
			}
			return object;
		}
		
		public static function clean():void {
			_loaders = null;
		}		
	}
}